﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web;


namespace StorageManage
{
    public static class SearchExpression
    {
        public static object ChangeType(object value, Type conversion)
        {
            var t = conversion;

            if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
            {
                if (value == null)
                {
                    return null;
                }

                t = Nullable.GetUnderlyingType(t);
            }

            return Convert.ChangeType(value, t);
        }
        private static Expression ConditonToExpression(SearchCondition condition, Expression parameter, Type type, string subValue)
        {
            Expression expr = null;
            PropertyInfo pi = type.GetProperty(condition.Field);
            if (pi != null)
            {
                Expression left = Expression.Property(parameter, pi);
                Type pType = pi.PropertyType;
                Expression right = null;
                object value = null;
                value = ChangeType(subValue, pi.PropertyType);
                //value = Convert.ChangeType(subValue, pi.PropertyType);
                right = Expression.Constant(value);
                Expression expr1 = null;
                ConstantExpression zero = Expression.Constant(0);
                switch (condition.Operator)
                {
                    case "=":
                        expr = Expression.Equal(left, right);
                        break;
                    case "<":
                        expr = Expression.LessThan(left, right);
                        break;
                    case "<=":
                        expr = Expression.LessThanOrEqual(left, right);
                        break;
                    case ">":
                        expr = Expression.GreaterThan(left, right);
                        break;
                    case ">=":
                        expr = Expression.GreaterThanOrEqual(left, right);
                        break;
                    case "!=":
                        expr = Expression.NotEqual(left, right);
                        break;
                    case "co":
                        expr = Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), right);
                        break;
                    case "noco":
                        MethodInfo methodNotLike = left.Type.GetMethod("Contains");
                        expr = Expression.Call(left, methodNotLike, new[] { right });
                        expr = Expression.Not(expr);
                        break;
                    case "start":
                        expr = Expression.Call(left, typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) }), right);
                        break;
                    case "nostart":
                        expr = Expression.Call(left, typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) }), right);
                        expr = Expression.Not(expr);
                        break;
                    case "end":
                        expr = Expression.Call(left, typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) }), right);
                        break;
                    case "str>=":
                        expr1 = Expression.Call(left, typeof(string).GetMethod("CompareTo", new Type[] { typeof(string) }), right);
                        expr = Expression.GreaterThanOrEqual(expr1, zero);
                        break;
                    case "str<=":
                        expr1 = Expression.Call(left, typeof(string).GetMethod("CompareTo", new Type[] { typeof(string) }), right);
                        expr = Expression.LessThanOrEqual(expr1, zero);
                        break;
                }
            }
            return expr;
        }
        public static Expression GetExprBody<T>(List<SearchCondition> conditions, ParameterExpression parameter, List<SearchCondition> highSearchList)
        {
            ConstantExpression cons = Expression.Constant(1);
            Expression body = Expression.Equal(cons, cons);
            Type type = typeof(T);
            if (conditions.Count > 0)
            {
                foreach (var item in conditions)
                {
                    var subBody = GetSubExpression<T>(item, parameter);
                    if (subBody != null)
                    {
                        body = Expression.And(body, subBody);
                    }
                }
            }
            if (highSearchList != null)
            {
                if (highSearchList.Count > 0)
                {
                    foreach (var item in highSearchList)
                    {
                        var highBody = GetSubExpression<T>(item, parameter);
                        if (highBody != null)
                        {
                            if (item.Logic == "or")
                            {
                                body = Expression.Or(body, highBody);
                            }
                            else
                            {
                                body = Expression.And(body, highBody);
                            }

                        }
                    }
                }
            }
            return body;
        }
        private static Expression GetSubExpression<T>(SearchCondition high, ParameterExpression parameter)
        {
            Type type = typeof(T);
            Expression body = null;
            Expression right = null;
            if (high.subConList != null && high.subConList.Count > 0)
            {
                foreach (SearchCondition subHigh in high.subConList)
                {
                    right = GetSubExpression<T>(subHigh, parameter);
                    if (body == null)
                    {
                        body = right;
                    }
                    else
                    {
                        if (subHigh.Logic == "or")
                        {
                            body = Expression.Or(body, right);
                        }
                        else
                        {
                            body = Expression.And(body, right);
                        }
                    }
                }
            }
            else
            {
                if (!string.IsNullOrEmpty(high.Field) && !string.IsNullOrEmpty(high.Operator))
                {
                    if (high.Value.Split(',').Length == 1)
                    {
                        body = ConditonToExpression(high, parameter, type, high.Value);
                    }
                    else
                    {
                        body = ConditonToExpression(high, parameter, type, high.Value.Split(',')[0]);
                        for (int j = 1; j < high.Value.Split(',').Length; j++)
                        {
                            Expression subRight = ConditonToExpression(high, parameter, type, high.Value.Split(',')[j]);
                            if (body != null)
                            {
                                body = Expression.Or(body, subRight);
                            }

                        }
                    }

                }
            }
            return body;
        }
        /// <summary>
        /// 按条件查询
        /// </summary>
        /// <typeparam name="T">实体模型</typeparam>
        /// <param name="conditions">过滤条件</param>
        /// <returns>表达式</returns>
        public static Expression<Func<T, bool>> FindByGroup<T>(List<SearchCondition> conditions)
        {
            ParameterExpression parameter = Expression.Parameter(typeof(T), "r");
            Expression body = GetExprBody<T>(conditions, parameter, null);
            if (body != null)
            {
                Expression<Func<T, bool>> expr = Expression.
                    Lambda<Func<T, bool>>(body, parameter);
                return expr;
            }
            return null;
        }
        /// <summary>
        /// 按条件查询
        /// </summary>
        /// <typeparam name="T">实体模型</typeparam>
        /// <param name="conditions">过滤条件</param>
        /// <returns>表达式</returns>
        public static Expression<Func<T, bool>> FindByGroup<T>(List<SearchCondition> conditions, List<SearchCondition> highSearchList)
        {
            ParameterExpression parameter = Expression.Parameter(typeof(T), "r");
            Expression body = GetExprBody<T>(conditions, parameter, highSearchList);
            if (body != null)
            {
                Expression<Func<T, bool>> expr = Expression.
                    Lambda<Func<T, bool>>(body, parameter);
                return expr;
            }
            return null;
        }
        /// <summary>
        /// 输入排序字段和相应类，返回排序表达式
        /// </summary>
        /// <typeparam name="类名"></typeparam>
        /// <param name="排序字段"></param>
        /// <returns></returns>
        public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string orderByProperty,
bool desc) where T : class
        {
            string methodName = desc ? "OrderBy" : "OrderByDescending";
            ParameterExpression parameter = Expression.Parameter(source.ElementType, String.Empty);
            MemberExpression property = Expression.Property(parameter, orderByProperty);
            LambdaExpression lambda = Expression.Lambda(property, parameter);

            Expression methodCallExpression = Expression.Call(typeof(Queryable), methodName,
                                            new Type[] { source.ElementType, property.Type },
                                            source.Expression, Expression.Quote(lambda));
            return source.Provider.CreateQuery<T>(methodCallExpression);
        }

        /// <summary>
        /// 返回sql语句where条件
        /// </summary>
        /// <param name="listSearch"></param>
        /// <returns></returns>
        public static string conStrBySearch(List<SearchCondition> listSearch)
        {
            string result = " 1=1 ";

            foreach (var item in listSearch)
            {
                if (item.Operator != null && item.Field != null && item.Value != null)
                {
                    result += " and  (";
                    for (int i = 0; i < item.Value.Split(',').Length; i++)
                    {
                        string subValues = item.Value.Split(',')[i];
                        result += item.Field;

                        if (item.Operator == "co")
                        {
                            result += " like '%" + subValues + "%'";
                        }
                        else if (item.Operator == "start")
                        {
                            result += " like '" + subValues + "%'";
                        }
                        else if (item.Operator == "end")
                        {
                            result += " like '%" + subValues + "'";
                        }
                        else if (item.Operator == "noco")
                        {
                            result += " not like '%" + subValues + "%'";
                        }
                        else
                        {
                            var operation = item.Operator;
                            if (operation.Contains("str"))
                            {
                                operation = operation.Remove(0, 3);
                            }
                            result += " " + operation + " '" + subValues + "'";
                        }
                        if (i != item.Value.Split(',').Length - 1)
                        {
                            result += " or ";
                        }
                    }
                    result += " )";
                }
            }
            return result;
        }
        public static string GetStrWhereByHigh(List<SearchCondition> listSearch)
        {
            string result = "1=1 ";

            foreach (var high in listSearch)
            {
                result = " " + GetSubStr(high, result);
            }
            return result;
        }
        private static string GetSubStr(SearchCondition high, string subStr)
        {
            subStr += " " + high.Logic;

            if (high.subConList != null && high.subConList.Count > 0)
            {
                subStr += "(";
                foreach (var subHigh in high.subConList)
                {
                    subStr = GetSubStr(subHigh, subStr);
                }
                subStr += " )";
            }
            else
            {

                int i = 0;

                if (i == 0)
                {
                    subStr += GetStrOperation(high, true);
                }
                else
                {
                    subStr += GetStrOperation(high, true);
                }
                i++;


            }
            return subStr;
        }
        private static string GetStrOperation(SearchCondition subHigh, bool p)
        {
            string s = " ";
            if (!p)
            {
                s += " " + subHigh.Logic;
            }
            switch (subHigh.Operator)
            {
                case "co":
                    s += " like  '%" + subHigh.Value + "%'";
                    break;
                case "start":
                    s += " like  '" + subHigh.Value + "%'";
                    break;
                case "end":
                    s += " like  '%" + subHigh.Value + "'";
                    break;
                case "noco":
                    s += " not like  '%" + subHigh.Value + "%'";
                    break;
                case "=":
                    s += " = '" + subHigh.Value + "'";
                    break;
                case ">":
                    s += " > '" + subHigh.Value + "'";
                    break;
                case "str>=":
                    s += " >= '" + subHigh.Value + "'";
                    break;
                case "<":
                    s += " < '" + subHigh.Value + "'";
                    break;
                case "str<=":
                    s += " <= '" + subHigh.Value + "'";
                    break;
            }
            return s;
        }
        public static List<SearchCondition> Search2High(List<SearchCondition> searchList)
        {
            List<SearchCondition> HighSearchList = new List<SearchCondition>() { };
            FindSubHighSearch(HighSearchList, searchList);
            return HighSearchList;
        }
        private static void FindSubHighSearch(List<SearchCondition> HighSearchList, List<SearchCondition> searchList)
        {
            var acSearchList = searchList.Where(r => r.state == null).ToList();

            foreach (var search in acSearchList)
            {
                if (string.IsNullOrEmpty(search.state))
                {
                    SearchCondition high = new SearchCondition() { Field = search.Field, Logic = search.Logic, Operator = search.Operator, Value = search.Value };

                    if (string.IsNullOrEmpty(search.Left))
                    {
                        search.state = "0";
                        HighSearchList.Add(high);
                    }
                    else if (search.Left == "(")
                    {
                        List<SearchCondition> subHighSearchList = new List<SearchCondition>() { };
                        search.Left = "";
                        FindSubHighSearch(subHighSearchList, searchList);

                        HighSearchList.Add(new SearchCondition()
                    {
                        Field = "",
                        Logic = search.Logic,
                        Operator = "",
                        Value = "",
                        subConList = subHighSearchList
                    });
                    }
                    if (search.Right == ")")
                    {
                        search.Right = "";
                        return;
                    }
                }
            }
            return;
        }
    }
}